﻿using NUnit.Framework;
using HappyTemplate;

namespace HappyTemplate.Tests
{
	[TestFixture]
	public class BuiltinFunctionTests : TestFixtureHelper
	{
		[Test]
		public void split()
		{
			Assert.AreEqual("1:2:3:4",
				HandledWriteTemplate(@"<$foreach(number in split(""1,2,3,4"", "","") between "":"") number next$>"));
		}

		[Test]
		public void formatNumber()
		{
			Assert.AreEqual("03.15", HandledWriteTemplate(@"<$formatNumber	(""00.##"", ""3.1459"")$>"));
		}

		[Test]
		public void isNull()
		{
			
			Template t = ParseTemplate(@"<$isNull(aNullValue) "" "" isNull(aNonNullValue)$>");
			_context["aNullValue"] = null;
			_context["aNonNullValue"] = 1;
			Assert.AreEqual("True False", HandledWriteTemplate(t));
		}

			[Test]
		public void coalesce()
		{
			TemplateSet ts =
				ParseTemplateSet(@"
performTest(mightBeNull)
<{<$
	if(isNull(mightBeNull))
		""<null>""
	else
		mightBeNull
			endif
$>}>
test()
<{<$
	performTest(coalesce(""a"", null, null, null)) "" ""
	performTest(coalesce(null, null, ""b"", null)) "" ""
	performTest(coalesce(null, null, null, ""c"")) "" ""
	performTest(coalesce(null, null, null, null))
$>}>
");	
			Assert.AreEqual("a b c <null>", HandledWriteTemplate(ts.GetTemplate("test")));
		}

		[Test]
		public void formatDate()
		{
			Assert.AreEqual("03/22/78 23:59:59",
				HandledWriteTemplate(@"<$formatDate(""March 22 1978 11:59:59 pm"", ""MM/dd/yy HH:mm:ss"")	$>"));
		}

		[Test]
		public void trimStart()
		{
			Assert.AreEqual("test ", HandledWriteTemplate(@"<$trimStart("" test "")$>"));
		}

		[Test]
		public void	trimEnd()
		{
			Assert.AreEqual(" test", HandledWriteTemplate(@"<$trimEnd("" test "")$>"));
		}

		[Test]
		public void trim()
		{
			Assert.AreEqual("test", HandledWriteTemplate(@"<$trim("" test "")$>"));
		}

		[Test]
		public void length()
		{	
			Assert.AreEqual("4", HandledWriteTemplate(@"<$length(""test"")$>"));
		}

		[Test]
		public void	substring()
		{
			Assert.AreEqual("is a", HandledWriteTemplate(@"<$	substring(""this is a test"", 5, 4)$>"));
		}
			
		[Test]
		public void	left()
		{
			Assert.AreEqual("this", HandledWriteTemplate(@"<$left(""this is a test"", 4)$>"));
		}

		[Test]
		public void right()
		{
			Assert.AreEqual("test", HandledWriteTemplate(@"<$right(""this is a test"", 4)$>"));
		}

		[Test]
		public void indexOf()
		{
			Assert.AreEqual("10", HandledWriteTemplate(@"<$indexOf(""this is a test"", ""test"")$>"));
		}

		[Test]
		public void lastIndexOf()
		{
																       //0123456789012345678901234567890
			Assert.AreEqual("21", HandledWriteTemplate(@"<$lastIndexOf(""testing... this is a test"", ""test"")$>"));
		}

		[Test]
		public void insert()
		{
			//0123456789012345678901234567890
			Assert.AreEqual("jack and jill went up the hill", 
				HandledWriteTemplate(@"<$insert(""jack and went up the hill"", "" jill"", 8)$>"));
		}

		[Test]
		public void contains()
		{
			//123456789012345678901234567890
			Assert.AreEqual("True False", HandledWriteTemplate
				(@"<$contains(""abcdefg"", ""cde"") "" "" contains(""abcdefg"", ""hij"")$>"));
		}

		[Test]
		public void repeat()
		{
			Assert.AreEqual("abababab", HandledWriteTemplate(@"<$repeat(""ab"", 4)$>"));
		}

		[Test]
		public void toLower()
		{
			Assert.AreEqual("lowercase", HandledWriteTemplate(@"<$toLower(""LoWeRcAsE"")$>"));
		}

		[Test]
		public void toUpper()
		{
			Assert.AreEqual("UPPERCASE", HandledWriteTemplate(@"<$toUpper(""uPpeRcAsE"")$>"));
		}

		[Test]
		public void invertCase()
		{
			Assert.AreEqual("iNvErT mE!", HandledWriteTemplate(@"<$invertCase(""InVeRt Me!"")$>"));
		}

		[Test]
		public void replace()
		{
			Assert.AreEqual("I am a good boy", 
				HandledWriteTemplate(@"<$replace(""I am a bad boy"", ""bad"", ""good"")$>"));
		}


		[Test]
		public void	padLeft()
		{
						   //0123456789
			Assert.AreEqual("     padme",
				HandledWriteTemplate(@"<$padLeft(""padme"", 10)$>"));
		}

		[Test]
		public void padRight()
		{
			//0123456789
			Assert.AreEqual("padme     ",
				HandledWriteTemplate(@"<$padRight(""padme"", 10)$>"));
		}

		[Test]
		public void startsWith()
		{
			Assert.AreEqual("True False", HandledWriteTemplate(
				@"<$ startsWith(""the beginning"", ""the"") "" "" startsWith(""anything"", ""the"")$>"));
		}

		[Test]
		public void endsWith()
		{
			Assert.AreEqual("True False", HandledWriteTemplate(
				@"<$ endsWith(""the beginning"", ""ing"") "" "" endsWith(""some value"", ""ing"")$>"));
		}

		[Test]
		public void reverse()
		{
			Assert.AreEqual("gfedcba", HandledWriteTemplate(@"<$ reverse(""abcdefg"")$>"));
		}

		[Test]
		public void isRegexMatch()
		{
			Assert.AreEqual("True False", HandledWriteTemplate(
				@"<$ isRegexMatch(""abcdef"", ""abc*"") "" "" isRegexMatch(""ghijklmn"", ""abc*"")$>"));	
		}

		[Test]
		public void	regexSplit()
		{
			//Pattern below is: zero or more whitespace chars, a comma, zero or more whitespace 
			Assert.AreEqual("1:2:3:4", HandledWriteTemplate(	
				@"<$foreach(number in regexSplit(""1\n,\n	2 \t, 3\t,\t4"", ""\\s*,\\s*"") between "":"") number next$>"));
		}

		[Test]
		public void regexReplace()
		{
			//Pattern below is: one or more whitespace chars
			Assert.AreEqual("1:2:3:4", HandledWriteTemplate(@"<$regexReplace(""1\n\n	2 \t 3\t\t4"", ""\\s+"", "":"")$>"));
		}


		
		[Test]
		public void isGreater1()
		{
			const string expected = @"True False False";
			const string actual = 
@"<$  
	/* test integer-integer comparisons */
	isGreater(1, 2) "" "" 
	isGreater(1, 1) "" "" 
	isGreater(2, 1) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isGreater2()
		{
			const string expected = @"True False False";
			const string actual = 
@"<$ 
	/* test double-double comparisons */
	isGreater(1.0, 2.0) "" "" 
	isGreater(1.0, 1.0) "" "" 
	isGreater(2.0, 1.0) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void	isGreater3()
		{
			const string expected = @"True False True False";
			const string actual = 
@"<$
	/* test integer-double comparisons */
	isGreater(1, 2.0) "" "" 
	isGreater(1, 1.0) "" "" 
	isGreater(1, 1.99) "" "" 
	isGreater(2, 1.0) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}
		[Test]
		public void isGreater4()
		{
			const string expected = @"True False False False False";
			const string actual =
@"<$ 
	/* test double-integer comparisons */
	isGreater(1.0, 2) "" "" 
	isGreater(1.0, 1) "" "" 
	isGreater(1.0, 1) "" "" 
	isGreater(1.1, 1) "" ""
	isGreater(2.0, 1)	
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isLess1()
		{
			const string expected = @"True False False";
			const string actual =
@"<$  
	/* test integer-integer comparisons */
	isLess(2, 1) "" "" 
	isLess(1, 1) "" "" 
	isLess(1, 2) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isLess2()
		{
			const string expected = @"True False False";
			const string actual =
@"<$ 
	/* test double-double comparisons */
	isLess(2.0, 1.0) "" "" 
	isLess(1.0, 1.0) "" "" 
	isLess(1.0, 2.0) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isLess3()
		{
			const string expected = @"True False False";
			const string actual =
@"<$
	/* test integer-double comparisons */
	isLess(2, 1.0) "" "" 
	isLess(1, 1.0) "" "" 
	isLess(1, 2.0) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}
		[Test]
		public void isLess4()
		{
			const string expected = @"True False True False";
			const string actual =
@"<$ 
	/* test double-integer comparisons */
	isLess(2.0, 1.0) "" "" 
	isLess(1.0, 1) "" "" 
	isLess(1.99, 1) "" "" 
	isLess(1.0, 2) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isGreaterOrEqual1()
		{
			const string expected = @"True True False";
			const string actual =
@"<$  
	/* test integer-integer comparisons */
	isGreaterOrEqual(1, 2) "" "" 
	isGreaterOrEqual(1, 1) "" "" 
	isGreaterOrEqual(2, 1) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isGreaterOrEqual2()
		{
			const string expected = @"True True False	";
			const string actual =
@"<$ 
	/* test double-double comparisons */
	isGreaterOrEqual(1.0, 2.0) "" "" 
	isGreaterOrEqual(1.0, 1.0) "" "" 
	isGreaterOrEqual(2.0, 1.0) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isGreaterOrEqual3()
		{
			const string expected = @"True True True False";
			const string actual =
@"<$
	/* test integer-double comparisons */
	isGreaterOrEqual(1, 2.0) "" "" 
	isGreaterOrEqual(1, 1.0) "" "" 
	isGreaterOrEqual(1, 1.99) "" "" 
	isGreaterOrEqual(2, 1.0) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}
		[Test]
		public void isGreaterOrEqual4()
		{
			const string expected = @"True True True False False";
			const string actual =
@"<$ 
	/* test double-integer comparisons */
	isGreaterOrEqual(1.0, 2) "" "" 
	isGreaterOrEqual(1.0, 1) "" "" 
	isGreaterOrEqual(1.0, 1) "" "" 
	isGreaterOrEqual(1.1, 1) "" ""
	isGreaterOrEqual(2.0, 1)	
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isLessOrEqual1()
		{
			const string expected = @"True True False";
			const string actual =
@"<$  
	/* test integer-integer comparisons */
	isLessOrEqual(2, 1) "" "" 
	isLessOrEqual(1, 1) "" "" 
	isLessOrEqual(1, 2) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isLessOrEqual2()
		{
			const string expected = @"True True False";
			const string actual =
@"<$ 
	/* test double-double comparisons */
	isLessOrEqual(2.0, 1.0) "" "" 
	isLessOrEqual(1.0, 1.0) "" "" 
	isLessOrEqual(1.0, 2.0) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isLessOrEqual3()
		{
			const string expected = @"True True False";
			const string actual =
@"<$
	/* test integer-double comparisons */
	isLessOrEqual(2, 1.0) "" "" 
	isLessOrEqual(1, 1.0) "" "" 
	isLessOrEqual(1, 2.0) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}
		[Test]
		public void isLessOrEqual4()
		{
			const string expected = @"True True True False";
			const string actual =
@"<$ 
	/* test double-integer comparisons */
	isLessOrEqual(2.0, 1.0) "" "" 
	isLessOrEqual(1.0, 1) "" "" 
	isLessOrEqual(1.99, 1) "" "" 
	isLessOrEqual(1.0, 2) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isEqual1()
		{
			const string expected = @"True False";
			const string actual = @"<$ isEqual(1, 1) "" "" isEqual(1, 2) $>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isEqual2()
		{
			const string expected = @"True False";
			const string actual = @"<$ isEqual(1.0, 1.0) "" "" isEqual(1.0, 1.1) $>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void	isEqual3()
		{
			const string expected = @"False False False True False";
			const string actual =
@"<$ 
	isEqual(1, 1.49) "" ""	
	isEqual(1, 1.5) "" ""	
	isEqual(1, 2.0) "" ""
	isEqual(1.0, 1) "" ""
	isEqual(1.0, 2) 
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void isEqual4()
		{
			const string expected = @"True False";
			const string actual = @"<$ isEqual(""value"", ""value"") "" "" isEqual(""A"", ""B"") $>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void	isOneOf()
		{
			const string expected = @"False True True True";
			const string actual =
@"<$ 
	isOneOf(1, 2, 3, 4, 5)		"" "" 
	isOneOf(2, 2, 3, 4, 5)		"" ""
	isOneOf(3, 2, 3, 4, 5)		"" ""
	isOneOf(1, 2, 3, 4, 5, 1)
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void	TestComparisonsWithVariables()
		{
			const string expected = @"True False True False True False True False False True";
			const string actual =
@"<$ 
	/* just run through the comparisons again to ensure they work with variables too */
	isLess(double2, double1) "" "" 
	isLess(double1, int1) "" "" 
	isLess(stringB, stringA) "" ""
	isGreater(double2, double1) "" "" 
	isGreater(double1, int2) "" "" 
	isGreater(stringB, stringA) "" ""
	isEqual(double1, int1) "" ""
	isEqual(double1, int2) "" ""
	isEqual(stringB, stringA) "" "" 
	isEqual(stringA, stringA) 
$>";

			Template template = ParseTemplate(actual);
			_context["int1"] = 1;
			_context["int2"] = 2;
			_context["double1"] = 1.0;
			_context["double1_99"] = 1.99;
			_context["double2"] = 2.0;
			_context["stringA"] = "A";
			_context["stringB"] = "B";

			Assert.AreEqual(expected, HandledWriteTemplate(template));
		}

		[Test]
		public void isSameInstance()
		{
			const string expected = @"True False";
			const string actual =
@"<$ 
	isSameInstance(anInstance, anInstance) "" ""
	isSameInstance(anInstance, anotherInstance)
$>";

			Template template = ParseTemplate(actual);
			_context["anInstance"] = new object();
			_context["anotherInstance"] = new object();

			Assert.AreEqual(expected, HandledWriteTemplate(template));
		}

		[Test] 
		public void newList_appendList()
		{

			const string expected = @"1 2 3 4 5 6";
			const string actual =
@"<$ 
	set aList = newList(1, 2, 3)
	appendList(aList, 4, 5, 6)
	foreach(number in aList between "" "")
		number 
	next
$>";

			Template template = ParseTemplate(actual);
			_context["anInstance"] = new object();
			_context["anotherInstance"] = new object();

			Assert.AreEqual(expected, HandledWriteTemplate(template));
		}

		[Test]
		public void isInList()
		{

			const string expected = @"True True True False";
			const string actual =
@"<$ 
	set aList = newList(1, 2, 3, 4, 5, 6)
	isInList(1, aList) "" ""
	isInList(3, aList) "" ""
	isInList(6, aList) "" ""
	isInList(7, aList)
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void sequence()
		{

			const string expected = @"1 2 3 4 5 6";
			const string actual =
@"<$ 
	foreach(number in sequence(1, 6) between "" "")
		number
	next
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void sequence_Reversed()
		{

			const string expected = @"6 5 4 3 2 1";
			const string actual =
@"<$ 
	foreach(number in sequence(6, 1) between "" "")
		number
	next
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void sequence_WithStep()
		{
			const string expected = @"2 4 6 8 10 12 14 16";
			const string actual =
@"<$ 
	foreach(number in sequence(2, 16, -2) between "" "")
		number
	next
$>";		//Note that the sequence is is ascending even though we pass -2 as the step

			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void sequence_WithStepReversed()
		{

			const string expected = @"16 14 12 10 8 6 4 2";
			const string actual =
@"<$ 
	foreach(number in sequence(16, 2, 2) between "" "")
		number
	next
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}

		[Test]
		public void sequence_WithToNotMultiple()
		{

			const string expected = @"0 2";
			const string actual =
@"<$ 
	foreach(number in sequence(0, 3, 2) between "" "")
		number
	next
$>";
			Assert.AreEqual(expected, HandledWriteTemplate(actual));
		}
	}	
}
